
#include <pthread.h>
#if defined(PLATFORM_IPHONE)
//#include <sys/semaphore.h>
#else
#include <semaphore.h>
#endif
#include <sys/time.h>
#include <glf/core/condition.h>
#include <glf/core/mutex.h>
#include <glf/core/spinLock.h>
#include <glf/core/atomic.h>
#include <glf/core/semaphore.h>

#include <glf/core/timer.h>

namespace glf {

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mutex
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Mutex::Impl {
	pthread_mutex_t mMutex;

	Impl(MutexType type) {
		if(type == MUTEX_RECURSIVE)
		{
			pthread_mutexattr_t attr;
			pthread_mutexattr_init(&attr);
			pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

			pthread_mutex_init(&mMutex, &attr);
			pthread_mutexattr_destroy(&attr);
		}
		else
		{
			GLF_ASSERT(type == MUTEX_NORMAL);
			pthread_mutex_init(&mMutex, NULL);
		}
	}

	~Impl() {
		pthread_mutex_destroy(&mMutex);
	}

	void Lock() {
		pthread_mutex_lock(&mMutex);
	}

	bool TryLock() {
		return pthread_mutex_trylock(&mMutex) == 0;
	}

	void Unlock() {
		pthread_mutex_unlock(&mMutex);
	}
};


////////////////////////////////////////////////////////////////////////////////////////////////////
// Condition
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Condition::Impl {
	Impl(Mutex &mutex) :
		mMutex(mutex) {
		pthread_cond_init(&Condition, NULL);
	}

	~Impl()
	{
		pthread_cond_destroy(&Condition);
	}

	int Signal() {
		if(!pthread_cond_signal(&Condition))
		{
			return 1;
		}
		return 0;
	}

	int Broadcast() {
		if(!pthread_cond_broadcast(&Condition))
		{
			return 1;
		}
		return 0;
	}

	int Wait(unsigned int time) {

		Mutex::Impl& impl = mMutex.GetImpl();

        int ret = 0;
		if(time == 0)
		{
			ret = pthread_cond_wait(&Condition, &impl.mMutex);
		}
		else
		{
			timespec ts;
			timeval  tp;

			gettimeofday(&tp, NULL);

			time_t secAdj = time_t(time / 1000);
			long nsecAdj = long(time % 1000) * 1000000;

			ts.tv_sec  = time_t(tp.tv_sec) + secAdj;
            ts.tv_nsec = long(tp.tv_usec) * 1000 + nsecAdj;

			ret = pthread_cond_timedwait(&Condition, &impl.mMutex, &ts);
		}
        return (ret == 0) ? 1 : 0;
	}

protected:
	Mutex &mMutex;
	pthread_cond_t Condition;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// SpinLock
////////////////////////////////////////////////////////////////////////////////////////////////////
//Fallback to Mutex

#if defined(GLF_PLATFORM_PLAYBOOK)
// GCC intrinsic does not link on Playbook, we'll use QNX's atomic lib
#include <arm/smpxchg.h>
struct SpinLock::Impl {

	Impl()
		: mLock(0) {
	}

	~Impl() {
	}

	void Lock() {
		int count;
		while(_smp_cmpxchg(&mLock, 0u, 1u) != 0) {
			count++; // Spend some time
		}
	}

	bool TryLock() {
		return _smp_cmpxchg(&mLock, 0u, 1u) == 0;
	}

	void Unlock() {
		_smp_cmpxchg(&mLock, 1u, 0u);
	}

private:
	unsigned int mLock;
};
#else
struct SpinLock::Impl {

	Impl()
		: mLock(0) {
	}

	~Impl() {
	}

	void Lock() {
		int count = 0;
		while(__sync_val_compare_and_swap(&mLock, 0, 1) != 0) {
			count++; // Spend some time
			if(count > 64)
			{
				glf::Thread::Sleep(0);
			}
		}
	}

	bool TryLock() {
		return __sync_bool_compare_and_swap(&mLock, 0, 1);
	}

	void Unlock() {
		__sync_val_compare_and_swap(&mLock, 1, 0);
	}

private:
	int mLock;
};
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// Semaphore
////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined(PLATFORM_IPHONE)
//While iOS and MacOSX have the header for unnamed posix semaphore, they don't actually work
//Fake semaphore with mutex + counter
struct Semaphore::Impl {
	Impl(unsigned int initialValue, unsigned int maxValue)
	{
		//sem_init(&semaphore, NULL, initialValue);
		currentValue = initialValue;
		maximumValue = maxValue;
		if(currentValue == 0)
			mMutex.Lock();
	}

	~Impl()
	{
	}

	int Wait()
	{
		mInternalMutex.Lock();

		if(currentValue > 1)
		{
			--currentValue;
			mInternalMutex.Unlock();
			return 0;
		}
		/*else if(currentValue == 1)
		{
			--currentValue;
			mMutex.Lock();
			mInternalMutex.Unlock();
			return 0;
		}*/

		mInternalMutex.Unlock();

		mMutex.Lock();
		--currentValue;

		return 0;
	}

	int TryWait()
	{
		mInternalMutex.Lock();

		if(currentValue > 1)
		{
			--currentValue;
			mInternalMutex.Unlock();
			return 0;
		}
		else if(currentValue == 1)
		{
			--currentValue;
			mMutex.Lock();
			mInternalMutex.Unlock();
			return 0;
		}

		mInternalMutex.Unlock();

		return -1;
	}

	int Post()
	{
		mInternalMutex.Lock();

		if(currentValue == 0)
		{
			++currentValue;
			mInternalMutex.Unlock();
			mMutex.Unlock();
			return 0;
		}
		else if(currentValue == maximumValue)
		{
			mInternalMutex.Unlock();
			return -1;
		}

		++currentValue;

		mInternalMutex.Unlock();

		return 0;
	}

	Mutex mMutex;
	Mutex mInternalMutex;
	unsigned int currentValue;
	unsigned int maximumValue;
};
#else
struct Semaphore::Impl {
	Impl(unsigned int initialValue, unsigned int maxValue)
	{
#if GLF_CONFIG_DEBUG
		int result =
#endif
		sem_init(&semaphore, 0, initialValue);
		currentValue = initialValue;
		maximumValue = maxValue;
#if GLF_CONFIG_DEBUG
		GLF_ASSERT(result == 0);
#endif
	}

	~Impl()
	{
#if GLF_CONFIG_DEBUG
		int result =
#endif
			sem_destroy(&semaphore);
#if GLF_CONFIG_DEBUG
		GLF_ASSERT(result == 0);
#endif
	}

	int Wait()
	{
		int waitResult = sem_wait(&semaphore);
		if(waitResult == 0)
		{
			--currentValue;
		}
#if GLF_CONFIG_DEBUG
		GLF_ASSERT(waitResult == 0);
#endif
		return waitResult;
	}

	int TryWait()
	{
		int waitResult = sem_trywait(&semaphore);
		if(waitResult == 0)
		{
			--currentValue;
		}
#if GLF_CONFIG_DEBUG
		GLF_ASSERT(waitResult == 0);
#endif
		return waitResult;
	}

	int Post()
	{
		++currentValue; //reserve token
		if(currentValue > maximumValue)
		{
			--currentValue;
			return -1; //cannot increase anymore
		}

		int postResult = sem_post(&semaphore);
#if GLF_CONFIG_DEBUG
		GLF_ASSERT(postResult == 0);
#endif
		if(postResult != 0)
			--currentValue;

		return postResult;
	}

	sem_t semaphore;
	unsigned int currentValue;
	unsigned int maximumValue;
};
#endif
} //namespace glf

